package com.ocm.core.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


import com.ocm.core.service.ClassDefService;
import com.ocm.core.service.OCMService;
import com.ocm.core.service.SecurityService;
import com.ocm.core.util.OCMUtil;
import com.ocm.core.mapping.DTOConfiguration;
import com.ocm.core.mapping.DTOService;
import com.ocm.core.mapping.DomainService;
import com.ocm.core.model.ClassInstance;
import com.ocm.core.dao.ClassDefDao;
import com.ocm.core.domain.ClassDefinition;
import com.ocm.core.domain.ClassDefinitionType;
import com.ocm.core.domain.PropertyDefinition;
import com.ocm.core.dto.ClassDefinitionDto;
import com.ocm.core.dto.PropertyDefinitionDto;
import com.ocm.core.dto.SecurityDto;
import com.ocm.core.dto.UserInfoDto;
import com.ocm.core.exception.OCMRuntimeException;
import com.ocm.core.helper.StorageHelper;

@Service("classDefService")
@Transactional(readOnly = true)
public class ClassDefServiceImpl implements ClassDefService {

   private ClassDefDao classDefDao;

   private DomainService domainService;

   private DTOService dtoService;

   private OCMService ocmService;
   
   private SecurityService securityService;

   @Autowired
   private StorageHelper storageHelper;

   @Autowired
   public ClassDefServiceImpl(
         @Qualifier("classDefDao")ClassDefDao classDefDao, 
         @Qualifier("ocmService")OCMService ocmService,
         @Qualifier("securityService")SecurityService securityService,
         @Qualifier("domainService")DomainService domainService,
         @Qualifier("dtoService") DTOService dtoService) {
      this.classDefDao = classDefDao;
      this.ocmService = ocmService;
      this.securityService = securityService;
      this.domainService = domainService;
      this.dtoService = dtoService;
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class, noRollbackFor= OCMRuntimeException.class)
   public void create(long parentClassDefId, ClassDefinitionDto classDefDto) {
      ClassDefinition classDef = this.domainService.convertClassDef(classDefDto);
      this.storageHelper.setUserInfo(classDef);
      ClassDefinition parent = this.classDefDao.findById(parentClassDefId);
      classDef.setParentClassDefUID(parent);
      classDef.setStorage(parent.getStorage());
      classDef.setClassDefinitionType(parent.getClassDefinitionType());
      ClassInstance classInstance = this.ocmService.createClassDefintion(classDef);
      classDef.setClassDefUID(classInstance.getNodeUUID());
      classDef.setRealPath(classInstance.getRealPath());
      try {
         this.classDefDao.save(classDef);
      }
      catch (Exception e) {
         this.ocmService.removeClassDefintion(classDef);
      }
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void update(ClassDefinitionDto classDefDto) {
      ClassDefinition classDef = this.domainService.convertClassDef(classDefDto);
      this.storageHelper.setUserInfo(classDef);
      this.classDefDao.save(classDef);
   }

   public List<ClassDefinitionDto> findAll(DTOConfiguration configuration) {
      List<ClassDefinition> classDefs = this.classDefDao.findAll();
      List<ClassDefinitionDto> classDefDtos = new ArrayList<ClassDefinitionDto>();
      for (ClassDefinition classDef : classDefs) {
         classDefDtos.add(this.dtoService.convertClassDto(classDef, configuration));
      }
      return classDefDtos;
   }

   public ClassDefinitionDto findId(long classDefId, DTOConfiguration configuration) {
      ClassDefinition classDef = this.classDefDao.findById(classDefId);
      ClassDefinitionDto classDefDto = this.dtoService.convertClassDto(classDef, configuration);
      List<SecurityDto> securityDtos = classDefDto.getSecurities(); 
      Long[] userIds = new Long[securityDtos.size()];
      for (int index=0; index<userIds.length; index++){
         userIds[index] = securityDtos.get(index).getUserId();
      }
      List<UserInfoDto> userInfoDtos = this.securityService.findAll(userIds);
      for (SecurityDto securityDto : securityDtos){
         securityDto.setUserInfo(OCMUtil.getUserInfoDto(securityDto.getUserId(), userInfoDtos));
      }
      return classDefDto;
   }

   public boolean hasExistName(long storage, String className) {
      ClassDefinition classDefinition = this.classDefDao.findName(storage, className);
      if (classDefinition != null)
         return true;
      return false;
   }

   public List<ClassDefinitionDto> findOriginalClass(long storageId, DTOConfiguration configuration) {
      List<ClassDefinition> classDefintions = this.classDefDao.findOriginalClass(storageId);
      List<ClassDefinitionDto> classDefDtos = this.dtoService.convertClassDefDtos(classDefintions, configuration);
      return classDefDtos;
   }

   public List<ClassDefinitionDto> findClassChilds(long parentClassDefId, DTOConfiguration configuration) {
      List<ClassDefinition> classDefintions = this.classDefDao.findByParentId(parentClassDefId);
      List<ClassDefinitionDto> classDefDtos = this.dtoService.convertClassDefDtos(classDefintions, configuration);
      return classDefDtos;
   }

   public List<PropertyDefinitionDto> getAllProperties(long classDefId) {
      ClassDefinition classDefinition = this.classDefDao.findById(classDefId);
      List<PropertyDefinition> properties = classDefinition.getAllProperties();
      List<PropertyDefinitionDto> propertiesDto = this.dtoService.convertPropertyDtos(properties, DTOConfiguration.instance());
      return propertiesDto;
   }

   public List<ClassDefinitionDto> findClassDefChilds(long parentClassDefId, DTOConfiguration configuration) {
      List<ClassDefinition> classDefintions = this.classDefDao.findByParentId(parentClassDefId,
            new ClassDefinitionType[] { ClassDefinitionType.DOCUMENTCLASSDEF, ClassDefinitionType.CUSTOMOBJECTDEF });
      List<ClassDefinitionDto> classDefDtos = this.dtoService.convertClassDefDtos(classDefintions, configuration);
      return classDefDtos;
   }
}
